package com.code.wordhelper.utils;

import com.code.wordhelper.model.ImageByteData;
import com.code.wordhelper.model.ImageData;
import com.code.wordhelper.testbean.Course;
import com.code.wordhelper.testbean.Person;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TableTemplateUtils {
    private final static String PLACE_HOLDER = "\\$\\{([\\w\\.]+)\\}";
    private final static String FIELD_PLACE_HOLDER = "\\[([\\w]+)\\]";
    private final static String MULTI_FIELD_PLACE_HOLDER = "\\[([\\w\\.]+)\\]";
    private final static Pattern FIELD_PLACE_HOLDER_PATTERN = Pattern.compile("\\[([\\w]+)\\]");
    private final static Pattern MULTI_FIELD_PLACE_HOLDER_PATTERN = Pattern.compile("\\[([\\w\\.]+)\\]");
    private final static Pattern PLACE_HOLDER_PATTERN = Pattern.compile(PLACE_HOLDER);

    private final static String PLACE_HOLDER_PREFIX = "$";
    private final static String PLACE_HOLDER_SUFFIX = "}";
    private final static String FIELD_PLACE_HOLDER_PREFIX = "[";
    private final static String FIELD_PLACE_HOLDER_SUFFIX = "]";
    private final static String MULTI_FIELD_SEPERATOR = ".";
    private final static String MULTI_FIELD_SEPERATOR_SPLIT = "\\.";
    private final static String FIELD_INDEX = "index";
    private final static String GAP_PIC = "    ";
    private final static String GAP_TEXT = "                    ";

    public static void replaceTables(XWPFDocument document, Map<String, Object> dataMap) throws Exception {
        List<XWPFTable> tableList = document.getTables();

        for (XWPFTable table : tableList) {
            XWPFTableCell firstTableCell = table.getRow(0).getCell(0);

            String tableHeaderInfo = firstTableCell.getText();
            //检查是否存在表格标志
            if (tableHeaderInfo.startsWith("${")) {
                String placeholder = PatternUtils.regex(tableHeaderInfo, PLACE_HOLDER_PATTERN, 1);
                if (!dataMap.containsKey(placeholder)) {
                    continue;
                }

                List<Object> objs = (List<Object>) dataMap.get(placeholder);

                replaceTableHeader(firstTableCell, dataMap);
                replaceTable(table, dataMap, objs);
            }
            if (tableHeaderInfo.startsWith("$${")) {
                String placeholder = PatternUtils.regex(tableHeaderInfo, PLACE_HOLDER_PATTERN, 1);
                if (!dataMap.containsKey(placeholder)) {
                    continue;
                }

                List<Object> objs = (List<Object>) dataMap.get(placeholder);

                replaceTableHeader(firstTableCell, dataMap);
                replaceComplextTable(table, dataMap, objs);
            }
        }
    }

    private static void replaceTable(XWPFTable table, Map<String, Object> dataMap, List<Object> objs) throws Exception {
        //查询需要需要循环的行
        XmlCursor xmlCursor = table.getCTTbl().newCursor();
        XWPFParagraph imgParagraph = null;
        XWPFParagraph textParagraph = null;

        List<XWPFTableRow> tableRows = table.getRows();
        int n = 0;
        int m = 0;
        for (int i = 1; i < tableRows.size(); i++) {
            //需要循环的标记
            int needRoundRow = -1;
            List<XWPFTableCell> tableCells = tableRows.get(i).getTableCells();
            for (XWPFTableCell tableCell : tableCells) {
                if (PatternUtils.match(tableCell.getText(), FIELD_PLACE_HOLDER)) {
                    needRoundRow = i;
                    break;
                }
            }

            // 找到匹配项，需要复制该行
            if (needRoundRow != -1) {
                for (int k = 0; k < objs.size(); k++) {
                    XWPFTableRow newRow = table.insertNewTableRow(needRoundRow + k + 1);
                    newRow.getCtRow().setTrPr(table.getRow(needRoundRow).getCtRow().getTrPr());

                    //复制行并解析属性
                    for (int j = 0; j < tableCells.size(); j++) {
                        XWPFTableCell tableCell = tableCells.get(j);
                        XWPFTableCell newCell = newRow.addNewTableCell();

                        if (PatternUtils.match(tableCell.getText(), MULTI_FIELD_PLACE_HOLDER) && tableCell.getText().contains(MULTI_FIELD_SEPERATOR)) {
                            // 如果存在循环属性,不做处理
                            continue;
                        } else if (PatternUtils.match(tableCell.getText(), FIELD_PLACE_HOLDER)) {
                            String field = PatternUtils.regex(tableCell.getText(), FIELD_PLACE_HOLDER_PATTERN, 1);
                            Object cellValue = null;
                            if (FIELD_INDEX.equalsIgnoreCase(field)) {
                                cellValue = String.valueOf(k + 1);
                            } else {
                                cellValue = ClassUtils.getFieldValue(objs.get(k), field);
                                if (cellValue == null) {
                                    continue;
                                }
                                if (cellValue instanceof ImageData) {
                                    if (m % 2 == 0) {
                                        xmlCursor.toEndToken();
                                        xmlCursor.toNextToken();
                                        imgParagraph = table.getBody().insertNewParagraph(xmlCursor);
                                        imgParagraph.setAlignment(ParagraphAlignment.CENTER);

                                        xmlCursor.toEndToken();
                                        xmlCursor.toNextToken();
                                        textParagraph = table.getBody().insertNewParagraph(xmlCursor);
                                        textParagraph.setAlignment(ParagraphAlignment.CENTER);
                                        //设置左右边距
//                                        textParagraph.setIndentationLeft(100);
//                                        textParagraph.setIndentationRight(100);
                                    }
                                    if (m % 2 != 0) {
                                        //每行只存放两张图片
                                        imgParagraph.createRun().setText(GAP_PIC, 0);
                                        textParagraph.createRun().setText(GAP_TEXT, 0);
                                    }

                                    ImageData imageData = (ImageData) cellValue;
                                    XWPFRun run = imgParagraph.createRun();
                                    run.addPicture(new FileInputStream(imageData.getUrl()), format(imageData.getExt()).nativeId, imageData.getFilename(), Units.toEMU(imageData.getHeight()), Units.toEMU(imageData.getWidth()));

                                    cellValue = "图片" + (n + 1) + "-" + (++m);
                                    textParagraph.createRun().setText(cellValue.toString(), 0);
                                }
                                if (cellValue instanceof ImageByteData) {
                                }
                            }

                            copyCellAndSetValue(tableCell, newCell, cellValue);
                        } else if (PatternUtils.match(tableCell.getText(), PLACE_HOLDER)) {
                            // 如果是普通属性
                            String prop = PatternUtils.regex(tableCell.getText(), PLACE_HOLDER_PATTERN, 1);
                            copyCellAndSetValue(tableCell, newCell, dataMap.get(prop).toString());
                        } else {
                            copyCellAndSetValue(tableCell, newCell, "");
                        }
                    }
                    i++;
                }

                // 删除模板行
                table.removeRow(needRoundRow);
                i--;
            } else {
                //如果没有需要循环的属性则查看是否有文本替换的标记
                for (XWPFTableCell tableCell : tableCells) {
                    if (PatternUtils.match(tableCell.getText(), PLACE_HOLDER)) {
                        // 如果是普通属性
//                        replaceCellTemplate(tableCell, dataMap);
                    }
                }
            }
        }
    }

    /**
     * 替换双重属性表格
     *
     * @param table
     * @param results
     * @param dataMap
     * @throws Exception
     */
    public static void replaceComplextTable(XWPFTable table, Map<String, Object> dataMap, List results) throws Exception {
        //查询需要需要循环的行
        List<XWPFTableRow> tableRows = table.getRows();

        List<XWPFTableCell> tableCells = table.getRow(0).getTableCells();
        int[] ones = new int[tableCells.size()];
        int n = table.getNumberOfRows();

        int oldRowLen = tableRows.size();
        List<Integer> deleteRow = new ArrayList<>();
        for (int i = 1; i < oldRowLen; i++) {
            int needRoundRow = -1;
            tableCells = table.getRow(i).getTableCells();
            for (XWPFTableCell tableCell : tableCells) {
                if (PatternUtils.match(tableCell.getText(), MULTI_FIELD_PLACE_HOLDER)) {
                    needRoundRow = i;
                    break;
                }
            }

            // 找到匹配项，需要复制该行
            if (needRoundRow != -1) {
                deleteRow.add(needRoundRow);
                for (Object result : results) {
                    setNull(ones, tableCells.size());

                    List fieldValueList = ClassUtils.getCollectionFieldName(result);
                    if (fieldValueList == null) {
                        continue;
                    }
                    for (int k = 0; k < fieldValueList.size(); k++) {
                        //复制行并解析属性
                        XWPFTableRow newRow = table.insertNewTableRow(n + k);
                        newRow.getCtRow().setTrPr(table.getRow(needRoundRow).getCtRow().getTrPr());

                        for (int j = 0; j < tableCells.size(); j++) {
                            XWPFTableCell tableCell = tableCells.get(j);
                            XWPFTableCell newCell = newRow.addNewTableCell();


                            //如果是一层
                            if (PatternUtils.match(tableCell.getText(), FIELD_PLACE_HOLDER)) {
                                ones[j] = 1;
                                copyCell(tableCell, newCell);
                            } else if (PatternUtils.match(tableCell.getText(), MULTI_FIELD_PLACE_HOLDER)) {
                                String multiField = PatternUtils.regex(tableCell.getText(), MULTI_FIELD_PLACE_HOLDER_PATTERN, 1);
                                String[] multiFields = multiField.split(MULTI_FIELD_SEPERATOR_SPLIT);
                                Object fieldValue = ClassUtils.getFieldValue(fieldValueList.get(k), multiFields[1]);
                                if (fieldValue == null) {
                                    fieldValue = "";
                                }
                                copyCellAndSetValue(tableCell, newCell, fieldValue.toString());
                            } else {
                                copyCellAndSetValue(tableCell, newCell, "");
                            }
                        }
                    }
                    // 解析一层属性
                    for (int s = 0; s < tableCells.size(); s++) {
                        if (1 == ones[s]) {
                            TableTools.mergeCellsVertically(table, s, n, n + fieldValueList.size());
                            String key = PatternUtils.regex(tableCells.get(s).getText(), FIELD_PLACE_HOLDER_PATTERN, 1);
//                            CellTemplateUtils.replaceParagraphs(table.getRow(n).getCell(s).getParagraphs(), ClassUtils.getFieldValue(result, key), FIELD_PLACE_HOLDER_PREFIX, FIELD_PLACE_HOLDER_SUFFIX, FIELD_PLACE_HOLDER);
                            setCellValue(table.getRow(n).getCell(s), ClassUtils.getFieldValue(result, key), 1, FIELD_PLACE_HOLDER_PREFIX, FIELD_PLACE_HOLDER_SUFFIX, FIELD_PLACE_HOLDER);
                        }
                    }
                    n = n + fieldValueList.size();
                }
            } else {
                //复制行数据
                copyRow(table, i, n);
                for (int j = 0; j < tableCells.size(); j++) {
                    XWPFTableCell tableCell = table.getRow(n).getCell(j);

                    if (PatternUtils.match(tableCell.getText(), PLACE_HOLDER)) {
                        String key = PatternUtils.regex(tableCell.getText(), PLACE_HOLDER);
                        setCellValue(tableCell, dataMap.get(key), 1, PLACE_HOLDER_PREFIX, PLACE_HOLDER_SUFFIX, PLACE_HOLDER);
//                        CellTemplateUtils.replaceParagraphs(tableCell.getParagraphs(), dataMap.get(key), PLACE_HOLDER_PREFIX, PLACE_HOLDER_SUFFIX, PLACE_HOLDER);
                    }
                }
                n++;
            }
        }
        // 删除模板行
        for (int i = oldRowLen - 1; i > 0; i--) {
            table.removeRow(i);
        }
    }

    private static void setNull(int[] ones, int len) {
        for (int s = 0; s < len; s++) {
            ones[s] = -1;
        }
    }

    private static void copyCellAndSetValue(XWPFTableCell sourceCell, XWPFTableCell targetCell, Object value) throws Exception {
        copyCell(sourceCell, targetCell);
        setCellValue(targetCell, value, 1, FIELD_PLACE_HOLDER_PREFIX, FIELD_PLACE_HOLDER_SUFFIX, MULTI_FIELD_PLACE_HOLDER);
    }

    private static void setCellValue(XWPFTableCell targetCell, Object value, int ratio, String prefix, String suffix, String placeholder) throws Exception {
        if (value instanceof ImageByteData) {
            ImageByteData imageByteData = (ImageByteData) value;

            byte[] bus = null;
            String ext = "jpeg";
            Matcher matcher = PatternUtils.BASE64_IMG_PATTERN.matcher(imageByteData.getBase64());
            if (matcher.find()) {
                ext = matcher.group(1);
                String rep = matcher.group(0);

                String imgBase64 = imageByteData.getBase64().replaceAll(rep, "");
                BASE64Decoder decoder = new BASE64Decoder();
                bus = decoder.decodeBuffer(imgBase64);
            }

            //dxa转换为emu
            int wid = Units.toEMU(targetCell.getWidthDecimal() / 21);
            for (int i = 0; i < targetCell.getParagraphs().size(); i++) {
                targetCell.removeParagraph(i);
            }

            targetCell.addParagraph().insertNewRun(0).addPicture(new ByteArrayInputStream(bus), format(ext).nativeId, imageByteData.getFilename(), wid, wid);
        } else {
            CellTemplateUtils.replaceParagraphs(targetCell.getParagraphs(), value, prefix, suffix, placeholder);
        }
    }

    private static void copyCell(XWPFTableCell sourceCell, XWPFTableCell targetCell) {
        //段落属性
        List<XWPFParagraph> sourceCellParagraphs = sourceCell.getParagraphs();
        if (sourceCellParagraphs == null || sourceCellParagraphs.size() <= 0) {
            return;
        }

        // 设置列的样式位模板的样式
        targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
        for (int i = 0; i < targetCell.getParagraphs().size(); i++) {
            targetCell.removeParagraph(i);
        }

        for (int i = 0; i < sourceCell.getParagraphs().size(); i++) {
            XWPFParagraph sourcePar = sourceCellParagraphs.get(i);
            XWPFParagraph targetPar = targetCell.addParagraph();

            targetPar.getCTP().setPPr(sourcePar.getCTP().getPPr());

            List<XWPFRun> sourceParRuns = sourcePar.getRuns();
            if (sourceParRuns != null && sourceParRuns.size() > 0) {
                // 如果当前cell中有run
                List<XWPFRun> runs = sourcePar.getRuns();

                for (int j = 0; j < runs.size(); j++) {
                    XWPFRun run = runs.get(j);
                    XWPFRun targetRun = targetPar.insertNewRun(j);
                    targetRun.getCTR().setRPr(run.getCTR().getRPr());

                    targetRun.setText(run.getText(0), 0);
                }
            }
        }
    }

    private static void copyRow(XWPFTable table, int source, int insertRowIndex) {
        XWPFTableRow sourceRow = table.getRow(source);
        XWPFTableRow targetRow = table.insertNewTableRow(insertRowIndex);
        targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
        List<XWPFTableCell> sourceCells = sourceRow.getTableCells();
        for (XWPFTableCell sourceCell : sourceCells) {
            XWPFTableCell newCell = targetRow.addNewTableCell();
            copyCell(sourceCell, newCell);
        }
    }

    private static void replaceTableHeader(XWPFTableCell tableCell, Map<String, Object> dataMap) throws Exception {
        List<XWPFParagraph> paragraphs = tableCell.getParagraphs();
        if (paragraphs == null || paragraphs.size() <= 0) {
            tableCell.setText(null);
        }

        List<XWPFRun> runList = tableCell.getParagraphArray(0).getRuns();

        if (CollectionUtils.isEmpty(runList)) {
            return;
        }
        int prev = 0;
        int prevIdx = 0;
        int last = -1;
        int lastIdx = 0;
        for (int i = 0; i < runList.size(); i++) {
            if (StringUtils.isEmpty(runList.get(i).text())) {
                continue;
            }
            String runText = runList.get(i).text();
            if (runText.contains(PLACE_HOLDER_SUFFIX)) {
                lastIdx = runText.indexOf(PLACE_HOLDER_SUFFIX);
                last = i;

                replaceRunTemplate(tableCell.getParagraphArray(0), runList, prev, last, prevIdx, lastIdx);
                return;
            }
        }
    }

    /**
     * 删除${}之间多余的内容
     *
     * @param runList
     * @param prev
     * @param last
     * @param prevIdx
     * @param lastIdx
     */
    private static void replaceRunTemplate(XWPFParagraph paragraph, List<XWPFRun> runList, int prev, int last, int prevIdx, int lastIdx) throws Exception {
        String prevRunText = runList.get(prev).text();
        String lastRunText = runList.get(last).text();

        if (prev == last) {
            // 如果${}位于同一行，则删除key
            if (lastIdx == prevRunText.length() - 1) {
                runList.get(last).getCTR().newCursor().removeXml();
            } else {
                runList.get(prev).setText(prevRunText.substring(lastIdx + 1), 0);
            }
        } else {
            // 如果${}不位于同一行，则删除多余的run
            if (lastIdx == lastRunText.length() - 1) {
                runList.get(last).getCTR().newCursor().removeXml();
            } else {
                runList.get(last).setText(lastRunText.substring(lastIdx + 1), 0);
            }

            for (int i = prev; i < last; i++) {
                runList.get(i).getCTR().newCursor().removeXml();
            }
        }
    }

    private static PictureData.PictureType format(String ext) {
        if ("JPEG".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.JPEG;
        } else if ("PNG".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.PNG;
        } else if ("SVG".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.SVG;
        } else if ("GIF".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.GIF;
        }
        return PictureData.PictureType.JPEG;
    }


    public static void main(String[] args) throws Exception {
        Course c1 = new Course("一年级", "语文");
        Course c2 = new Course("一年级", "数学");
        Course c3 = new Course("二年级", "英语");
        Course c4 = new Course("二年级", "语文");
        Course c5 = new Course("二年级", "思政");

        List<ImageByteData> imageByteDatas = buildImageData();
        Map<String, Object> dataMap = new HashMap<>();
        Person p1 = new Person("张三", "男", "教师", "大学", Arrays.asList(c1, c2), imageByteDatas.get(0));
        Person p2 = new Person("李四", "女", "校长", "博士", Arrays.asList(c3, c4, c5), imageByteDatas.get(1));

        List<Object> persons = Arrays.asList(p1, p2);
        dataMap.put("persons", persons);
        dataMap.put("teacherNum", 2);
        dataMap.put("courseNum", 5);

        ImageData imageData = new ImageData();
        imageData.setUrl("C:\\Users\\10419\\Desktop\\aaa.jpg");
        imageData.setExt("jpg");
        imageData.setFilename("aaa.jpg");
        dataMap.put("image", imageData);

        OPCPackage pack = POIXMLDocument.openPackage("C:\\Users\\10419\\Desktop\\test.docx");
        XWPFDocument document = new XWPFDocument(pack);//创建word对象

        replaceTables(document, dataMap);

        FileOutputStream fos = new FileOutputStream("C:\\Users\\10419\\Desktop\\test1.docx");
        document.write(fos);
        fos.close();
    }

    private static List<ImageByteData> buildImageData() {
        List<String> pics = Base64Utils.getPic();
        Base64.Encoder encoder = Base64.getEncoder();
        List<ImageByteData> imageByteDatas = new ArrayList<>(pics.size());
        pics.forEach((pic) -> {
            ImageByteData imageByteData = new ImageByteData();
            imageByteData.setBase64(pic);
            imageByteData.setFilename("aaa");
            imageByteData.setExt("");
            imageByteDatas.add(imageByteData);
        });

        return imageByteDatas;
    }
}
